package com.ming.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ming.dao.ProdEsDao;
import com.ming.domain.Prod;
import com.ming.es.ProdEs;
import com.ming.service.ImportService;
import com.ming.service.ProdService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * @Author 武汉尚学堂
 * 功能描述： mysql的商品信息 导入es里面去
 * 目的：因为mysql的访问量达不到要求  需要使用es支持用户的正常访问
 * 场景：商品如何导入es（如果商家新上架一个商品，如果用户购买了商品）
 * 实现：
 * 1.全量导入  mysql直接把商品全部导入es （发生在项目启动的时候（也可以自己触发）,多线程批量分页导入 5000条 1000页 ，多线程 for(i=1;i<=1000;i++) {} ）
 * 2.增量导入  把mysql中修改的商品导入es中 （商家新增一个商品 过30分钟 1000w  用户就可以查到了，定时任务，查询出来分批分页导入？  update_time ，t1  t2）
 * 3.快速导入  特殊场景需要及时同步es的数据（使用 mq  做一个队列 快速帮我们实现导入功能 ）
 * <p>
 * 导入的功能 怎么划分微服务？
 * 1.单独写一个导入服务   （按照功能划分）  ---- 需要 prod实体类 （或者依赖 product-api）
 * 2.放在商品模块  （按照商品信息）  -- 感觉这个模块 太累了  他还需要结合es
 * 3.搜索模块   （按照使用中间件的形式划分  es ）  -- （或者依赖 product-api）  难道远程调用吗？
 * <p>
 * 原则
 * 考虑性能    性能为王
 * 商品模块是最好的选择
 * <p>
 * 还有其他方式
 * 数据库同步中间件的形式    全量  触发器 增量 回溯
 * <p>
 * 导入es
 * 首先要有一个es的对象
 *
 * 导入总结
 * mysql -- java -- es
 * 使用数据同步中间件  canal
 *
 */
@Service
@Slf4j
public class ImportServiceImpl implements ImportService, CommandLineRunner {

    @Autowired
    private ProdEsDao prodEsDao;

    @Autowired
    private ProdService prodService;

    /**
     * 起始时间
     */
    private Date t1;

    /**
     * 每页的条数
     */
    @Value("${esimport.size}")
    private Integer size;

    /**
     * 全量导入 项目奖一起动 分批分页多线程实现
     * 1.自定义分页
     * 1.1 查询总条数
     * 2.2 计算分页 int page =  total % size == 0 ? total / size : ((total / size)+1)
     * 2. 循环 fori
     * 3. 查询数据库，Prod  ProdEs  对象转换一下
     * 4. 导入es
     */
    @Override
    public void importAll() {
        // 1.查询总条数
        Integer totalCount = prodService.getTotal(null, null);
        if (totalCount == null || totalCount == 0) {
            log.info("全量导入 商品数量为0");
            return;
        }
        // 2.计算分页
        int page = totalCount % size == 0 ? totalCount / size : ((totalCount / size) + 1);
        // 3.循环使用mybatis循环mysql
        for (int i = 1; i <= page; i++) {
            // 查询mysql 导入的方法
            importProdToEs(i, size, null, null);
        }
        log.info("导入完成");
        // 记录一下t1   11.27
        t1 = new Date();
    }


    /**
     * 查询导入方法
     *
     * @param current
     * @param size
     * @param t1
     * @param t2
     */
    private void importProdToEs(int current, Integer size, Date t1, Date t2) {
        // 1.组装分页对象  分页查询需要发几个sql  ？ limit  总条数  我这里分页查询 不需要总条数 这是一个小小的优化
        Page<Prod> page = new Page<>(current, size, false);
        // 2.查询prod表
        List<Prod> prodList = prodService.findProdPageByTime(page, t1, t2);
        // 3.组装对象
        ArrayList<ProdEs> prodEsArrayList = new ArrayList<>(prodList.size() * 2);
        prodList.forEach(prod -> {
            ProdEs prodEs = new ProdEs();
            BeanUtil.copyProperties(prod, prodEs, true);
            prodEsArrayList.add(prodEs);
        });
        // 4.导入es
        prodEsDao.saveAll(prodEsArrayList);
    }

    /**
     * 增量导入 商家上新
     * 定时任务
     *
     * @Scheduled （initialDelay:第一次执行的时间， fixedRate:第一次执行完以后 后面每隔多久执行一次 ）
     * 2分钟导入一次
     * update_time 区间查询  11.25 -- 11.27   11.29   11.31
     */
    @Override
    @Scheduled(initialDelay = 120 * 1000, fixedRate = 120 * 1000)
    public void importUpdate() {
        log.info("增量导入开始");
        // 每次执行增量导入 时间就往后推移
        Date t2 = new Date();
        // 1.查询总条数
        Integer totalCount = prodService.getTotal(t1, t2);
        if (totalCount == null || totalCount == 0) {
            // 把时间窗口往后推移  11.29    2033 - 11.29
            t1 = t2;
            log.info("增量导入 商品数量为0");
            return;
        }
        // 2.计算分页
        int page = totalCount % size == 0 ? totalCount / size : ((totalCount / size) + 1);
        // 3.循环使用mybatis循环mysql
        for (int i = 1; i <= page; i++) {
            // 查询mysql 导入的方法
            importProdToEs(i, size, t1, t2);
        }
        log.info("导入完成");
        // 把时间窗口往后推移
        t1 = t2;
    }

    /**
     * 快速导入 特殊情况 修改数据
     * 集合mq
     */
    @Override
    public void importQuick() {

    }

    /**
     * Callback used to run the bean.
     *
     * @param args incoming main method arguments
     * @throws Exception on error
     */
    @Override
    public void run(String... args) throws Exception {
        importAll();
    }
}
